package com.evol.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.evol.domain.frp.PluginContent;
import com.evol.domain.frp.Response;
import com.evol.domain.frp.UserInfo;
import com.evol.enums.HandlerMessage;
import com.evol.service.TokenService;
import com.evol.util.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.List;

/**
 * frp 通讯类
 * @author EvOL
 * @description
 * @date 2024-05-31 14:14
 **/
@Controller
@RequestMapping("/handler")
public class HandleController extends BaseController{
    @Value("${evol.lang}")
    private String lang;
    @Autowired
    private TokenService tokenService;

    @PostMapping()
    @ResponseBody
    public String handler(HttpServletRequest request,String version, String op, @RequestBody String data) {
        System.out.println(data);
        if(StringUtils.isEmpty(data)){
            return Response.error("data is not null").getString();
        }
        JSONObject json=JSONObject.parseObject(data);
        String content=json.getJSONObject("content").toJSONString();
        String opstr=json.getString("op");
        if("Login".equals(opstr)){
            return handleLogin(JSON.parseObject(content,PluginContent.LoginContent.class)).getString();
        }else if("NewWorkConn".equals(opstr)){
            return handleNewWorkConn(JSON.parseObject(content,PluginContent.NewWorkConnContent.class)).getString();
        }else if("NewUserConn".equals(opstr)){
            return handleNewUserConn(JSON.parseObject(content,PluginContent.NewUserConnContent.class)).getString();
        }else if("NewProxy".equals(opstr)){
            return handleNewProxy(JSON.parseObject(content,PluginContent.NewProxyContent.class)).getString();
        }else if("Ping".equals(opstr)){
            return handlePing(JSON.parseObject(content,PluginContent.PingContent.class)).getString();
        }
        return Response.success().getString();
    }

    /**
     * 登陆校验
     * @param content
     * @return
     */
    public Response handleLogin(PluginContent.LoginContent content) {
        String token = content.metas.token;
        String user = content.user;
        return judgeToken(user, token);
    }

    /**
     * 新代理连接
     * @param content
     * @return
     */
    public Response handleNewProxy(PluginContent.NewProxyContent content) {
        String token = content.user.metas.token;
        String user = content.user.user;
        Response judgeToken = judgeToken(user, token);
        if (judgeToken.reject) {
            return judgeToken;
        }
        return judgePort(content);
    }

    /**
     * 心跳
     * @param content
     * @return
     */
    public Response handlePing(PluginContent.PingContent content) {
        String token = content.user.metas.token;
        String user = content.user.user;
        return judgeToken(user, token);
    }

    /**
     * 新连接
     * @param content
     * @return
     */
    public Response handleNewWorkConn(PluginContent.NewWorkConnContent content) {
        String token = content.user.metas.token;
        String user = content.user.user;
        return judgeToken(user, token);
    }

    /**
     * 新用户连接
     * @param content
     * @return
     */
    public Response handleNewUserConn(PluginContent.NewUserConnContent content) {
        String token = content.user.metas.token;
        String user = content.user.user;
        return judgeToken(user, token);
    }


    private Response judgeToken(String user, String token) {
        Response res = new Response();
        if (tokenService.getTokenSize()==0) {
            res.unchange = true;
        } else if (user == null || user.isEmpty() || token == null || token.isEmpty()) {
            res.reject = true;
            res.rejectReason = HandlerMessage.UTEMPTY.getMessage(lang);
        } else {
            UserInfo info = tokenService.getToken(user);
            if (info == null) {
                res.reject = true;
                res.rejectReason = String.format(HandlerMessage.UNEXIST.getMessage(lang),user);
            } else if (!info.enable) {
                res.reject = true;
                res.rejectReason = String.format(HandlerMessage.UDISENABLE.getMessage(lang),user);
            } else if (!info.token.equals(token)) {
                res.reject = true;
                res.rejectReason = String.format(HandlerMessage.UINVALID.getMessage(lang),user);
            } else {
                res.unchange = true;
            }
        }
        return res;
    }
    private Response judgePort(PluginContent.NewProxyContent content) {
        Response res = new Response();
        String proxyType = content.proxyType;
        int userPort = content.remotePort;
        String[] userDomains = content.customDomains;
        String userSubdomain = content.subDomain;
        String user = content.user.user;
        boolean portAllowed = true;
        boolean domainAllowed = true;
        boolean subdomainAllowed = true;
        boolean reject = false;
        StringBuilder portErr = new StringBuilder();

        List<String> supportProxyTypes = Arrays.asList("tcp", "tcpmux", "udp", "http", "https");

        if (!supportProxyTypes.contains(proxyType)) {
            System.out.printf("proxy type [%s] not support, plugin do nothing", proxyType);
            res.unchange = true;
            return res;
        }

        if (proxyType.equals("tcp") || proxyType.equals("udp")) {
            portAllowed = false;
            UserInfo token = tokenService.getToken(user);
            if (token != null) {
                for (Object port : token.ports) {
                    if (port instanceof String) {
                        String str = (String) port;
                        if (str.contains("-")) {
                            String[] allowedRanges = str.split("-");
                            if (allowedRanges.length != 2) {
                                portErr.append(String.format(HandlerMessage.UPRFE.getMessage(lang), user, port));
                                break;
                            }
                            try {
                                int start = Integer.parseInt(allowedRanges[0].trim());
                                int end = Integer.parseInt(allowedRanges[1].trim());
                                if (userPort >= start && userPort <= end) {
                                    portAllowed = true;
                                    break;
                                }
                            } catch (NumberFormatException e) {
                                portErr.append(String.format(HandlerMessage.UPRFEN.getMessage(lang), user, port));
                                break;
                            }
                        } else {
                            if (str.isEmpty()) {
                                portAllowed = true;
                                break;
                            }
                            try {
                                int allowed = Integer.parseInt(str);
                                if (allowed == userPort) {
                                    portAllowed = true;
                                    break;
                                }
                            } catch (NumberFormatException e) {
                                portErr.append(String.format(HandlerMessage.UAPNN.getMessage(lang), user, port));
                            }
                        }
                    } else if (port instanceof Integer) {
                        int allowed = (int) port;
                        if (allowed == userPort) {
                            portAllowed = true;
                            break;
                        }
                    }
                }
            } else {
                portAllowed = true;
            }
        }

        if (!portAllowed) {
            if (portErr.length() == 0) {
                portErr.append(String.format(HandlerMessage.UPNA.getMessage(lang), user, userPort));
            }
            reject = true;
        }

        if (proxyType.equals("http") || proxyType.equals("https") || proxyType.equals("tcpmux")) {
            if (portAllowed) {
                UserInfo token = tokenService.getToken(user);
                if (token != null) {
                    if (StringUtils.isEmpty(token.domains[0])) {
                        domainAllowed = true;
                    } else {
                        for (String userDomain : userDomains) {
                            if (!userDomain.equals(token.domains)) {
                                domainAllowed = false;
                                break;
                            }
                        }
                    }
                }
                if (!domainAllowed) {
                    portErr.append(String.format(HandlerMessage.UDNA.getMessage(lang), user, String.join(",", userDomains)));
                    reject = true;
                }
            }
        }

        if (proxyType.equals("http") || proxyType.equals("https")) {
            subdomainAllowed = false;
            if (portAllowed && domainAllowed) {
                UserInfo token = tokenService.getToken(user);
                if (token != null) {
                    if (StringUtils.isEmpty(token.subdomains[0])) {
                        subdomainAllowed = true;
                    } else {
                        for (String subdomain : token.subdomains) {
                            if (subdomain.equals(userSubdomain)) {
                                subdomainAllowed = true;
                                break;
                            }
                        }
                    }
                } else {
                    subdomainAllowed = true;
                }
                if (!subdomainAllowed) {
                    portErr.append(String.format(HandlerMessage.USDNA.getMessage(lang), user, userSubdomain));
                    reject = true;
                }
            }
        }

        if (reject) {
            res.reject = true;
            res.rejectReason = portErr.toString();
        } else {
            res.unchange = true;
        }
        return res;
    }
}
